Outlearn API v1 Reference
- Category Level 2
- Billing
- Helpjuice logos
- Font Awesome
- Product Updates
- Getting Started
- Category from test
- Editor
- Users
- Design
- Aziz Cat
- sena test internal
- test 134
- test 11111
- gabriel-category
- Design
- Test test
- Billing-copy-85
- Editor-copy-526
- Pablo Test
- BLOG-Test
- オンライン署名の開始
- TestingNewCat
- TestingNegativeCount
Table of Contents
Base URL: https://data.outlearn.com/api/v1
Authentication
All API v1 endpoints require a Bearer token in the Authorization header:
Authorization: Bearer <api-key>The API key is linked to a specific Agent and Account. All requests are automatically scoped to that agent's account.
Response Formats
Resource
{
"data": { ... }
}
Collection
{
"data": [ ... ],
"pagination": {
"cursor": "eyJpZCI6NDJ9",
"has_more": true
}
}
Error
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": { "field": ["error message"] }
}
}
Pagination
All collection endpoints use cursor-based (keyset) pagination.
| Parameter | Type | Default | Description |
|---|---|---|---|
cursor |
string | — | Opaque cursor from a previous response's pagination.cursor
|
limit |
integer | 20 | Results per page (1–100) |
The response pagination object contains:
-
cursor— Pass this value as thecursorquery parameter to fetch the next page.nullwhen no more results. -
has_more—trueif more results exist beyond this page.
Error Codes
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST |
Missing required parameter or invalid input |
| 401 | UNAUTHORIZED |
Missing or invalid API key |
| 404 | NOT_FOUND |
Resource not found |
| 422 | UNPROCESSABLE_ENTITY |
Validation error or business rule violation |
| 500 | INTERNAL_ERROR |
Unexpected server error |
Agents
List Agents
GET /api/v1/agentsReturns the agent associated with the authenticated API key.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
cursor |
string | No | Pagination cursor |
limit |
integer | No | Results per page (default: 20, max: 100) |
Response 200 OK
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Support Bot",
"description": "Handles customer support inquiries",
"status": "active",
"created_at": "2026-01-15T10:30:00.000Z",
"updated_at": "2026-02-20T14:15:00.000Z"
}
],
"pagination": {
"cursor": null,
"has_more": false
}
}
Retrieve Agent
GET /api/v1/agents/{id}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
uuid | Yes | Agent's external identifier (must match authenticated agent) |
Response 200 OK
{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Support Bot",
"description": "Handles customer support inquiries",
"status": "active",
"created_at": "2026-01-15T10:30:00.000Z",
"updated_at": "2026-02-20T14:15:00.000Z"
}
}Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
id does not match the authenticated agent |
Update Agent
PATCH /api/v1/agents/{id}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
uuid | Yes | Agent's external identifier (must match authenticated agent) |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | No | Agent name |
description |
string | No | Agent description |
status |
string | No |
"active" or "inactive"
|
Request Example
{
"name": "Updated Support Bot",
"status": "inactive"
}Response 200 OK
{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Updated Support Bot",
"description": "Handles customer support inquiries",
"status": "inactive",
"created_at": "2026-01-15T10:30:00.000Z",
"updated_at": "2026-03-14T09:00:00.000Z"
}
}Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
id does not match the authenticated agent |
Contacts
List Contacts
GET /api/v1/contactsReturns contacts scoped to the authenticated account.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
email |
string | No | Filter contacts by exact email match |
cursor |
string | No | Pagination cursor |
limit |
integer | No | Results per page (default: 20, max: 100) |
Response 200 OK
{
"data": [
{
"id": 1,
"name": "Jane Smith",
"email": "jane@example.com",
"guest": false,
"metadata": {
"company": "Acme Corp",
"plan": "enterprise"
},
"created_at": "2026-02-01T08:00:00.000Z",
"updated_at": "2026-03-10T12:30:00.000Z"
}
],
"pagination": {
"cursor": "eyJpZCI6MX0",
"has_more": false
}
}Response Fields
| Field | Type | Description |
|---|---|---|
id |
integer | Contact ID |
name |
string | Contact name |
email |
string | Contact email (null for guest contacts) |
guest |
boolean |
true if the contact is an anonymous visitor |
metadata |
object | Custom key-value metadata |
created_at |
timestamptz | Creation timestamp |
updated_at |
timestamptz | Last update timestamp |
Retrieve Contact
GET /api/v1/contacts/{id}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | Contact ID |
Response 200 OK
{
"data": {
"id": 1,
"name": "Jane Smith",
"email": "jane@example.com",
"guest": false,
"metadata": { "company": "Acme Corp" },
"created_at": "2026-02-01T08:00:00.000Z",
"updated_at": "2026-03-10T12:30:00.000Z"
}
}Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
Contact does not exist in this account |
Create or Find Contact
POST /api/v1/contactsUses a find-or-create pattern. If a contact with the given email already exists in the account, it is returned (and updated with any provided fields). Otherwise a new contact is created.
If no email is provided, an anonymous guest contact is created.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | No | Contact email. Omit to create a guest contact. |
name |
string | No | Contact name |
metadata |
object | No | Custom metadata (deep-merged if contact exists) |
Request Example
{
"email": "jane@example.com",
"name": "Jane Smith",
"metadata": {
"company": "Acme Corp",
"plan": "enterprise"
}
}Response — New Contact 201 Created
{
"data": {
"id": 2,
"name": "Jane Smith",
"email": "jane@example.com",
"guest": false,
"metadata": { "company": "Acme Corp", "plan": "enterprise" },
"created_at": "2026-03-14T09:00:00.000Z",
"updated_at": "2026-03-14T09:00:00.000Z"
},
"created": true
}Response — Existing Contact 200 OK
{
"data": {
"id": 1,
"name": "Jane Smith",
"email": "jane@example.com",
"guest": false,
"metadata": { "company": "Acme Corp", "plan": "enterprise" },
"created_at": "2026-02-01T08:00:00.000Z",
"updated_at": "2026-03-14T09:00:00.000Z"
},
"created": false
}Update Contact
PATCH /api/v1/contacts/{id}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | Contact ID |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | No | Contact email |
name |
string | No | Contact name |
metadata |
object | No | Custom metadata (deep-merged with existing; set a key to null to remove it) |
Request Example
{
"name": "Jane Doe",
"metadata": {
"plan": "pro",
"company": null
}
}Response 200 OK
Returns the updated contact resource.
Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
Contact does not exist in this account |
Conversations
List Conversations
GET /api/v1/conversationsReturns conversations scoped to the authenticated account.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
contact_id |
integer | No | Filter by contact ID |
status |
string | No |
"active" or "ended"
|
cursor |
string | No | Pagination cursor |
limit |
integer | No | Results per page (default: 20, max: 100) |
Response 200 OK
{
"data": [
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"contact_id": 1,
"status": "active",
"metadata": { "source": "widget" },
"ended_at": null,
"created_at": "2026-03-14T08:00:00.000Z",
"updated_at": "2026-03-14T08:30:00.000Z"
}
],
"pagination": {
"cursor": null,
"has_more": false
}
}Response Fields
| Field | Type | Description |
|---|---|---|
id |
uuid | Conversation's external identifier |
contact_id |
integer | Associated contact ID |
status |
string |
"active" or "ended"
|
metadata |
object | Custom key-value metadata |
ended_at |
timestamptz | Timestamp when conversation was ended (null if active) |
created_at |
timestamptz | Creation timestamp |
updated_at |
timestamptz | Last update timestamp |
Retrieve Conversation
GET /api/v1/conversations/{id}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
uuid | Yes | Conversation's external identifier |
Response 200 OK
{
"data": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"contact_id": 1,
"status": "active",
"metadata": { "source": "widget" },
"ended_at": null,
"created_at": "2026-03-14T08:00:00.000Z",
"updated_at": "2026-03-14T08:30:00.000Z"
}
}Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
Conversation does not exist in this account |
Create Conversation
POST /api/v1/conversationsCreates a new conversation for a contact.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
contact_id |
integer | Yes | ID of the contact |
metadata |
object | No | Custom metadata |
Request Example
{
"contact_id": 1,
"metadata": {
"source": "api",
"session": "abc123"
}
}Response 201 Created
{
"data": {
"id": "b3d7e9a2-1c4f-4a8e-9b6d-2f5e8c1a3d7b",
"contact_id": 1,
"status": "active",
"metadata": { "source": "api", "session": "abc123" },
"ended_at": null,
"created_at": "2026-03-14T09:00:00.000Z",
"updated_at": "2026-03-14T09:00:00.000Z"
}
}Errors
| Status | Code | When |
|---|---|---|
| 422 | UNPROCESSABLE_ENTITY |
Validation failed (e.g., invalid contact_id) |
End Conversation
POST /api/v1/conversations/{id}/endCloses an active conversation. Once ended, no new messages can be added.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
uuid | Yes | Conversation's external identifier |
Response 200 OK
{
"data": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"contact_id": 1,
"status": "ended",
"metadata": { "source": "widget" },
"ended_at": "2026-03-14T10:00:00.000Z",
"created_at": "2026-03-14T08:00:00.000Z",
"updated_at": "2026-03-14T10:00:00.000Z"
}
}Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
Conversation does not exist |
| 422 | UNPROCESSABLE_ENTITY |
Conversation is already ended |
Messages
List Messages
GET /api/v1/conversations/{conversation_id}/messagesReturns user and assistant messages in a conversation (internal system messages are excluded).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
conversation_id |
uuid | Yes | Conversation's external identifier |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
cursor |
string | No | Pagination cursor |
limit |
integer | No | Results per page (default: 20, max: 100) |
Response 200 OK
{
"data": [
{
"id": 101,
"role": "user",
"content": "What is your return policy?",
"created_at": "2026-03-14T09:00:00.000Z"
},
{
"id": 102,
"role": "assistant",
"content": "Our return policy allows returns within 30 days of purchase...",
"created_at": "2026-03-14T09:00:05.000Z"
}
],
"pagination": {
"cursor": null,
"has_more": false
}
}Response Fields
| Field | Type | Description |
|---|---|---|
id |
integer | Message ID |
role |
string |
"user" or "assistant"
|
content |
string | Message content (source metadata stripped from assistant messages) |
created_at |
timestamptz | Creation timestamp |
Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
Conversation does not exist |
Create Message
POST /api/v1/conversations/{conversation_id}/messages
Sends a user message and generates an AI assistant response. Supports both synchronous and streaming modes.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
conversation_id |
uuid | Yes | Conversation's external identifier |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
content |
string | Yes | User message content (cannot be blank) |
stream |
boolean | No | Set to true for Server-Sent Events streaming (default: false) |
Request Example
{
"content": "What is your return policy?",
"stream": false
}
Synchronous Response (stream: false)
Response 201 Created
{
"data": {
"user_message": {
"id": 101,
"role": "user",
"content": "What is your return policy?",
"created_at": "2026-03-14T09:00:00.000Z"
},
"assistant_message": {
"id": 102,
"role": "assistant",
"content": "Our return policy allows returns within 30 days of purchase...",
"created_at": "2026-03-14T09:00:05.000Z"
}
}
}
Streaming Response (stream: true)
Response 200 OK Content-Type: text/event-stream
The response is delivered as Server-Sent Events (SSE). Events are sent in this order:
| Event | Description | Data Fields |
|---|---|---|
message.created |
User message was persisted | Full message object (id, role, content, created_at) |
stream.start |
Assistant response has begun |
message_id, role
|
stream.delta |
Content chunk |
delta (text fragment) |
stream.end |
Assistant response is complete |
message_id, content, created_at
|
Stream Example
event: message.created
data: {"id":101,"role":"user","content":"What is your return policy?","created_at":"2026-03-14T09:00:00.000Z"}
event: stream.start
data: {"message_id":102,"role":"assistant"}
event: stream.delta
data: {"delta":"Our return "}
event: stream.delta
data: {"delta":"policy allows "}
event: stream.delta
data: {"delta":"returns within 30 days of purchase..."}
event: stream.end
data: {"message_id":102,"content":"Our return policy allows returns within 30 days of purchase...","created_at":"2026-03-14T09:00:05.000Z"}
Errors
| Status | Code | When |
|---|---|---|
| 400 | BAD_REQUEST |
content is missing or blank |
| 404 | NOT_FOUND |
Conversation does not exist |
| 422 | UNPROCESSABLE_ENTITY |
Conversation has ended |
| 500 | INTERNAL_ERROR |
AI response generation failed |